/* eslint-disable no-param-reassign */
import {
  ref,
  VNode,
  PropType,
  onMounted,
  defineComponent,
  onUnmounted,
} from 'vue';
import {
  useUIStore,
  useNamespace,
  OverlayContainer,
} from '@ibiz-template/vue3-util';
import { isNumber } from 'lodash-es';
import {
  Modal,
  ViewMode,
  IModalData,
  IOverlayContainer,
} from '@ibiz-template/runtime';
import interact from 'interactjs';
import { calcOpenModeStyle, listenJSEvent } from '@ibiz-template/core';
import { IModalOptions } from '../../interface';
import './app-modal-component.scss';

export const AppModalComponent = defineComponent({
  props: {
    opts: {
      type: Object as PropType<IModalOptions>,
      default: () => ({}),
    },
  },
  setup(props, ctx) {
    const ns = useNamespace('modal');
    const isShow = ref(false);
    const { zIndex } = useUIStore();
    const modalZIndex = zIndex.increment();
    const modalRef = ref();
    /**
     * 模态样式
     */
    const { width, height } = props.opts;
    const modalStyle = {
      zIndex: modalZIndex,
      width: isNumber(width) ? calcOpenModeStyle(width, 'modal') : width,
      height: isNumber(height) ? calcOpenModeStyle(height, 'modal') : height,
    };

    const modal = new Modal({
      mode: props.opts.isRouteModal ? ViewMode.ROUTE_MODAL : ViewMode.MODAL,
      viewUsage: 2,
      dismiss: (data: IData): void | undefined => {
        zIndex.decrement();
        isShow.value = false;
        ctx.emit('dismiss', data);
      },
    });

    /**
     * 关闭抽屉外部调用
     * - 外部函数式调用
     */
    const dismiss = (_data?: IModalData): void => {
      modal.dismiss(_data);
    };

    /**
     * 显示抽屉外部调用
     * - 外部函数式调用
     */
    const present = (): void => {
      isShow.value = true;
    };

    /**
     * 关闭抽屉
     *
     */
    const onClosed = async (): Promise<void> => {
      await modal.dismiss();
    };

    /**
     * 点击遮罩关闭抽屉
     *
     */
    const onClickModal = (): void => {
      if (props.opts.closeOnClickModal !== false) {
        onClosed();
      }
    };

    /**
     * ESC关闭抽屉
     *
     * @param {KeyboardEvent} event
     */
    const listener = (event: KeyboardEvent): void => {
      if (event.code === 'Escape' && props.opts.closeOnPressEscape !== false) {
        onClosed();
      }
    };

    const cleanup = listenJSEvent(window, 'keydown', listener, {
      capture: true,
    });

    const dragBorder = (): void => {
      interact('.ibiz-modal__body').resizable({
        edges: {
          top: true,
          left: true,
          bottom: true,
          right: true,
        },
        listeners: {
          move: event => {
            const style = {
              transform: 'none',
              top: `${event.rect.top}px`,
              left: `${event.rect.left}px`,
              width: `${event.rect.width}px`,
              height: `${event.rect.height}px`,
            };
            Object.assign(event.target.style, style);
          },
          start: () => {
            // 禁止选择文本，避免拖动时出现选择效果
            document.body.style.userSelect = 'none';
          },
          end: () => {
            // 恢复选择文本功能
            document.body.style.userSelect = '';
          },
        },
        modifiers: [
          // 保持在父对象内部
          interact.modifiers.restrictEdges({ outer: document.body }),
        ],
      });
    };

    /**
     * 限制元素位置，使其不超出窗口边界
     * @param newLeft - 元素的新左侧位置
     * @param newTop - 元素的新上侧位置
     * @param bodyWidth - 元素的宽度
     * @param bodyHeight - 元素的高度
     * @param isTransformed - 元素是否有应用 transform 样式
     * @returns 返回修正后的新位置 [newLeft, newTop]
     */
    const limitPosition = (
      newLeft: number,
      newTop: number,
      bodyWidth: number,
      bodyHeight: number,
      isTransformed: boolean,
    ): [number, number] => {
      // 当元素没有变换时，使用 transform 影响
      if (isTransformed) {
        bodyWidth /= 2;
        bodyHeight /= 2;
      }
      const windowWidth = window.innerWidth;
      const windowHeight = window.innerHeight;

      // 普通边界限制
      newLeft = Math.max(0, Math.min(newLeft, windowWidth - bodyWidth));
      newTop = Math.max(0, Math.min(newTop, windowHeight - bodyHeight));

      // 如果元素是变换过的，处理左边界和上边界的限制
      if (isTransformed) {
        newLeft = Math.max(bodyWidth, newLeft);
        newTop = Math.max(bodyHeight, newTop);
      }

      return [newLeft, newTop];
    };

    const onDraggable = (element?: HTMLElement): void => {
      if (!element) return;
      const body = modalRef.value!.querySelector('.ibiz-modal__body');
      // 获取元素的位置信息
      element.onmousedown = (e: MouseEvent): void => {
        // 禁止选择文本，避免拖动时出现选择效果
        document.body.style.userSelect = 'none';
        const offsetX = e.clientX - body.offsetLeft;
        const offsetY = e.clientY - body.offsetTop;

        const onMouseMove = (evt: MouseEvent): void => {
          const rect = body.getBoundingClientRect();

          // 限制元素位置并更新
          const [newLeft, newTop] = limitPosition(
            evt.clientX - offsetX,
            evt.clientY - offsetY,
            rect.width,
            rect.height,
            body.style.transform !== 'none',
          );

          // 更新元素位置
          body.style.left = `${newLeft}px`;
          body.style.top = `${newTop}px`;
        };

        const onMouseUp = (): void => {
          // 恢复选择文本功能
          document.body.style.userSelect = '';
          document.removeEventListener('mousemove', onMouseMove);
          document.removeEventListener('mouseup', onMouseUp);
        };

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
      };
    };

    /**
     * 拖拽
     * @description 通过定时器去获取插槽中视图头
     */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    let timer: any;
    const draggable = (): void => {
      timer = setInterval(() => {
        let draggableNode = modalRef.value?.querySelector(
          props.opts.draggableClass || '.ibiz-modal__body >.ibiz-view',
        );
        if (draggableNode) {
          if (timer) clearInterval(timer);
          if (!props.opts.draggableClass)
            // 未指定时默认拖拽视图头
            draggableNode = draggableNode.querySelector(
              '.ibiz-panel-view-header',
            );
          //  指定拖拽节点权重最高
          if (draggableNode) onDraggable(draggableNode);
        }
      }, 300);
    };

    onMounted(() => {
      if (props.opts.dragBorder !== false) dragBorder();
      if (props.opts.draggable !== false) draggable();
    });

    onUnmounted(() => {
      cleanup();
      if (timer) clearInterval(timer);
    });

    return {
      ns,
      modal,
      isShow,
      modalRef,
      modalStyle,
      present,
      dismiss,
      onClosed,
      onClickModal,
    };
  },
  render() {
    return (
      <div
        ref='modalRef'
        class={[this.ns.b(), this.ns.is('show', this.isShow)]}
      >
        {this.opts.modal !== false && (
          <div
            class={[this.ns.e('overlay'), this.opts.modalClass]}
            onClick={this.onClickModal}
            style={{ zIndex: this.modalStyle.zIndex - 1 }}
          ></div>
        )}
        <div
          class={[
            this.ns.e('body'),
            this.opts.customClass,
            this.ns.is('show-close', this.opts.showClose),
          ]}
          style={this.modalStyle}
        >
          {this.opts.showClose !== false && (
            <div
              title='关闭'
              onClick={this.onClosed}
              class={this.ns.em('icon', 'close')}
            >
              <ion-icon name='close-outline'></ion-icon>
            </div>
          )}
          {this.$slots.default?.(this.modal)}
        </div>
      </div>
    );
  },
});

/**
 * 创建模态框
 *
 * @export
 * @param {() => VNode} render
 * @param {(IModalOptions | undefined)} [opts]
 * @return {*}  {IOverlayContainer}
 */
export function createModal(
  render: () => VNode,
  opts?: IModalOptions | undefined,
): IOverlayContainer {
  return new OverlayContainer(AppModalComponent, render, opts);
}
